/*
 * FileName：MyServiceImpl.java 
 * <p>
 * Copyright (c) 2017-2020 <a href="https://www.smartwx.info">hermit(1154808491@qq.com)</a>.
 * <p>
 * Licensed under the GNU General Public License, Version 3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/gpl-3.0.html
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package com.smart.wx.wxapi.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smart.common.exception.WxError;
import com.smart.common.exception.WxErrorException;
import com.smart.common.util.WxMemoryCacheClient;
import com.smart.wx.common.util.wx.WxUtil;
import com.smart.wx.wxapi.process.*;
import com.smart.wx.wxapi.service.MyService;
import com.smart.wx.wxapi.vo.*;
import com.smart.wx.wxcms.dao.*;
import com.smart.wx.wxcms.entity.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 业务消息处理
 * 开发者根据自己的业务自行处理消息的接收与回复；
 */

@Service
public class MyServiceImpl implements MyService {

	private Logger logger = LoggerFactory.getLogger(MyServiceImpl.class);
    @Resource
    private MsgBaseMapper msgBaseDao;

    @Resource
    private MsgTextMapper msgTextDao;

    @Resource
    private MediaFilesMapper mediaFilesMapper;

    @Resource
    private AccountMenuMapper menuDao;

    @Resource
    private AccountMenuGroupMapper menuGroupDao;

    @Resource
    private AccountFansMapper fansDao;

    @Resource
    private UserTagMapper userTagDao;
	@Resource
	private ReplyAutoMapper replyAutoDao;
	@Resource
	private ReplyKeyMapper replyKeyDao;
	@Resource
	private ReplyKeywordMapper replyKeywordDao;
	@Resource
	private MsgNewsMapper msgNewsMapper;
	@Resource
	private MsgArticleMapper msgArticleMapper;

    /**
     * 处理消息
     * 开发者可以根据用户发送的消息和自己的业务，自行返回合适的消息；
     * @param msgRequest : 接收到的消息
     * @param mpAccount ： appId
     */
    public String processMsg(MsgRequest msgRequest, MpAccount mpAccount)  throws WxErrorException {
        String msgtype = msgRequest.getMsgType();// 接收到的消息类型
        String respXml = null;// 返回的内容；
        //这里不应该从缓存中去公众号
//        String account=WxMemoryCacheClient.getAccount();
        String account=msgRequest.getToUserName();
        //先判断事件
       if (msgtype.equals(MsgType.Event.toString())) {// 事件消息
            /**
             * 用户订阅公众账号、点击菜单按钮的时候，会触发事件消息
             */
            respXml = this.processEventMsg(msgRequest, mpAccount);

            // 文本消息，判断是否判断是否匹配关键字
        } else  if (msgtype.equals(MsgType.Text.toString())) {
            /**
             * 文本消息，一般公众号接收到的都是此类型消息
             */
            respXml = this.processTextMsg(msgRequest, mpAccount);
        } else {//其他消息统一回复消息自动回复
        	ReplyAuto replyAuto=new ReplyAuto();
        	replyAuto.setType(0);//0代表消息自动回复
        	replyAuto.setAccount(account);
        	ReplyAuto auto=null;
        	try {
				List<ReplyAuto> listAuto = replyAutoDao.queryList(replyAuto);
				if(listAuto!=null&&!listAuto.isEmpty()){
					auto=listAuto.get(0);
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
        	if(auto!=null){
        		if(auto.getMsgType().equals("text")){
        			respXml = processReply(msgRequest, auto.getMsgType(), auto.getContent());
        		}else {
        			respXml = processReply(msgRequest, auto.getMsgType(), auto.getMediaId());
        		}
        	}
        }

        // 如果没有对应的消息，默认返回订阅消息；
        if (StringUtils.isEmpty(respXml)) {
            MsgBase base = msgBaseDao.getRandomMsg(MsgType.SUBSCRIBE.toString(),mpAccount.getAccount());
            if(base != null) {
                return buildMsg(base,msgRequest,mpAccount);
            }
        }
        return respXml;
    }

    /**
     * 自动回复判断方法提取
     * @param type
     * @param data 文本内容或者mediaid
     */
    private String processReply(MsgRequest msgRequest, String type, String data){
    	String respXml = null;// 返回的内容；
		if(type.equals("text")){
			MsgResponseText reponseText =new MsgResponseText();
			reponseText.setContent(data);
			reponseText.setToUserName(msgRequest.getFromUserName());
			reponseText.setFromUserName(msgRequest.getToUserName());
			reponseText.setMsgType(MsgType.Text.toString());
			reponseText.setCreateTime(new Date().getTime()/1000);
			respXml = MsgXmlUtil.textToXml(reponseText);
		}else if(type.equals("news")){
			List<MsgNews> listNews = msgNewsMapper.getByMediaId(data);
    		//判断数据库是否存在数据
    		if(listNews!=null&&!listNews.isEmpty()){
    			MsgNews news=listNews.get(0);
    			List<MsgArticle> msgNews = news.getArticles();
    			MsgResponseNews resNews=WxMessageBuilder.getMsgResponseNews(msgRequest, msgNews);
    			respXml = MsgXmlUtil.newsToXml(resNews);
    		}
		}else {
			MediaFiles media = mediaFilesMapper.getFileByMediaId(data);
			//判断是否被删除
			if(media!=null){
				if(type.equals("image")){
					MsgResponseImage image=	WxMessageBuilder.getMsgResponseImage(msgRequest, media);
					respXml = MsgXmlUtil.imageToXml(image);
				}else if(type.equals("voice")){
					MsgResponseVoice voice = WxMessageBuilder.getMsgResponseVoice(msgRequest, media);
					respXml = MsgXmlUtil.voiceToXml(voice);
				}else if(type.equals("video")){
					MsgResponseVideo video = WxMessageBuilder.getMsgResponseVideo(msgRequest, media);
					respXml = MsgXmlUtil.videoToXml(video);
				}
			}
		}
    	return respXml;
    }
    // 处理文本消息
    private String processTextMsg(MsgRequest msgRequest, MpAccount mpAccount) {
        String content = msgRequest.getContent();
      //这里不应该从缓存中去公众号
//        String account=WxMemoryCacheClient.getAccount();
        String account=msgRequest.getToUserName();
        String respXml = null;// 返回的内容；
        if (!StringUtils.isEmpty(content)) {// 文本消息
            String tmpContent = content.trim();
            // 根据关键词获取消息
            ReplyKey replyKey = replyKeyDao.queryObjectByKey(tmpContent, account);
            if(replyKey!=null){
            	if(replyKey.getMsgType().equals("text")){
        			respXml = processReply(msgRequest, replyKey.getMsgType(), replyKey.getContent());
        		}else {
        			respXml = processReply(msgRequest, replyKey.getMsgType(), replyKey.getMediaId());
        		}
            }else{//匹配不到则走消息自动回复
            	ReplyAuto replyAuto=new ReplyAuto();
            	replyAuto.setType(0);//0代表消息自动回复
            	replyAuto.setAccount(account);
            	ReplyAuto auto=null;
            	try {
    				List<ReplyAuto> listAuto = replyAutoDao.queryList(replyAuto);
    				if(listAuto!=null&&!listAuto.isEmpty()){
    					auto=listAuto.get(0);
    				}
    			} catch (Exception e) {
    				logger.error(e.getMessage(), e);
    			}
            	if(auto!=null){
            		if(auto.getMsgType().equals("text")){
            			respXml = processReply(msgRequest, auto.getMsgType(), auto.getContent());
            		}else {
            			respXml = processReply(msgRequest, auto.getMsgType(), auto.getMediaId());
            		}
            	}
            }
        }
        return respXml;
    }

    // 处理事件消息
    private String processEventMsg(MsgRequest msgRequest, MpAccount mpAccount) throws WxErrorException {
        String key = msgRequest.getEventKey();
        if (MsgType.SUBSCRIBE.toString().equals(msgRequest.getEvent())) {// 订阅消息
            logger.info("关注者openId----------" + msgRequest.getFromUserName());
            String openId = msgRequest.getFromUserName();
            AccountFans fans = WxApiClient.syncAccountFans(openId, mpAccount);
            // 用户关注微信公众号后更新粉丝表
            if (null != fans) {
                AccountFans tmpFans = fansDao.getByOpenId(openId,mpAccount.getAccount());
                if (tmpFans == null) {
                    fans.setAccount(mpAccount.getAccount());
                    fans.setCreateTime(new Date());
                    fansDao.add(fans);
                } else {
                    fans.setId(tmpFans.getId());
                    fansDao.update(fans);
                }
            }
            ReplyAuto replyAuto=new ReplyAuto();
            replyAuto.setType(1);//0代表消息自动回复
            replyAuto.setAccount(mpAccount.getAccount());
            ReplyAuto auto=null;
            try {
                List<ReplyAuto> listAuto = replyAutoDao.queryList(replyAuto);
                if(listAuto!=null&&!listAuto.isEmpty()){
                    auto=listAuto.get(0);
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            if (auto != null) {
                if(auto.getMsgType().equals("text")){
                    return processReply(msgRequest, auto.getMsgType(), auto.getContent());
                }else {
                    return  processReply(msgRequest, auto.getMsgType(), auto.getMediaId());
                }
            }
        } else if (MsgType.UNSUBSCRIBE.toString().equals(msgRequest.getEvent())) {// 取消订阅消息
            //删除u粉丝信息
            logger.info("关注者openId----------" + msgRequest.getFromUserName());
            String openId = msgRequest.getFromUserName();
            AccountFans accountFans = new AccountFans();
            accountFans.setAccount(mpAccount.getAccount());
            accountFans.setOpenId(openId);
            fansDao.delete(accountFans);
        } else {// 点击事件消息
            if (!StringUtils.isEmpty(key)) {
                /**
                 * 固定消息
                 * _fix_ ：在我们创建菜单的时候，做了限制，对应的event_key 加了 _fix_
                 * 
                 * 当然开发者也可以进行修改
                 */
                if (key.startsWith("_fix_")) {
                    String baseIds = key.substring("_fix_".length());
                    if (!StringUtils.isEmpty(baseIds)) {
                        String[] idArr = baseIds.split(",");
                        if (idArr.length > 1) {// 多条图文消息
                            List<MsgArticle> msgNews = msgBaseDao.listMsgNewsByBaseId(idArr,mpAccount.getAccount());
                            if (msgNews != null && msgNews.size() > 0) {
                                return MsgXmlUtil.newsToXml(WxMessageBuilder.getMsgResponseNews(msgRequest, msgNews));
                            }
                        } else {// 图文消息，或者文本消息
                            MsgBase base = msgBaseDao.getById(baseIds);
                            if(base != null) {
                                return buildMsg(base,msgRequest,mpAccount);
                            }
//                            if (msg.getMsgtype().equals(MsgType.Text.toString())) {
//                                MsgText text = msgBaseDao.getMsgTextByBaseId(baseIds);
//                                if (text != null) {
//                                    return MsgXmlUtil.textToXml(WxMessageBuilder.getMsgResponseText(msgRequest, text));
//                                }
//                            } else {
//                                List<MsgArticle> msgNews = msgBaseDao.listMsgNewsByBaseId(idArr,mpAccount.getAccount());
//                                if (msgNews != null && msgNews.size() > 0) {
//                                    return MsgXmlUtil.newsToXml(WxMessageBuilder.getMsgResponseNews(msgRequest, msgNews));
//                                }
//                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    // 发布菜单
    public JSONObject publishMenu(MpAccount mpAccount) throws WxErrorException {
    	//获取对应账号下的菜单
    	AccountMenu acm=new AccountMenu();
    	acm.setAccount(mpAccount.getAccount());
    	// 获取数据库菜单
        List<AccountMenu> menus = menuDao.listWxMenus(acm);
        Matchrule matchrule = new Matchrule();
        String menuJson = JSONObject.toJSONString(WxUtil.prepareMenus(menus, matchrule));
        logger.info("创建菜单传参如下:" + menuJson);
        JSONObject rstObj = WxApiClient.publishMenus(menuJson, mpAccount);// 创建普通菜单
        logger.info("创建菜单返回消息如下:" + rstObj.toString());
        // 以下为创建个性化菜单demo，只为男创建菜单；其他个性化需求 设置 Matchrule 属性即可
        // matchrule.setSex("1");//1-男 ；2-女
        // JSONObject rstObj = WxApiClient.publishAddconditionalMenus(menuJson,mpAccount);//创建个性化菜单

        // if(rstObj != null){//成功，更新菜单组
        // if(rstObj.containsKey("menu_id")){
        // menuGroupDao.updateMenuGroupDisable();
        // menuGroupDao.updateMenuGroupEnable(gid);
        // }else if(rstObj.containsKey("errcode") && rstObj.getInt("errcode") == 0){
        // menuGroupDao.updateMenuGroupDisable();
        // menuGroupDao.updateMenuGroupEnable(gid);
        // }
        // }
        return rstObj;
    }

    // 删除菜单
    public JSONObject deleteMenu(MpAccount mpAccount)  throws WxErrorException {
        JSONObject rstObj = WxApiClient.deleteMenu(mpAccount);
        if (rstObj != null && rstObj.getIntValue("errcode") == 0) {// 成功，更新菜单组
            menuGroupDao.updateMenuGroupDisable();
        }
        return rstObj;
    }

    // 获取用户列表
    public boolean syncAccountFansList(MpAccount mpAccount) throws WxErrorException {
        String nextOpenId = null;
        //修改策略，先删除再添加
        AccountFans entity=new AccountFans();
        entity.setAccount(mpAccount.getAccount());
        fansDao.delete(entity);
        return doSyncAccountFansList(nextOpenId, mpAccount);
    }

    // 同步粉丝列表(开发者在这里可以使用递归处理)
    private boolean doSyncAccountFansList(String nextOpenId, MpAccount mpAccount) throws WxErrorException {
        String url = WxApi.getFansListUrl(WxApiClient.getAccessToken(mpAccount), nextOpenId);
        logger.info("同步粉丝入参消息如下:" + url);
        JSONObject jsonObject = WxApi.httpsRequest(url, HttpMethod.POST, null);
        logger.info("同步粉丝返回消息如下:" + jsonObject.toString());
        if (jsonObject.containsKey("errcode")) {
            return false;
        }
        List<AccountFans> fansList = new ArrayList<AccountFans>();
        if (jsonObject.containsKey("data")) {
            if (jsonObject.getJSONObject("data").containsKey("openid")) {
                JSONArray openidArr = jsonObject.getJSONObject("data").getJSONArray("openid");
                int length = openidArr.size();
                for (int i = 0; i < length; i++) {
                    Object openId = openidArr.get(i);
                    AccountFans fans = WxApiClient.syncAccountFans(openId.toString(), mpAccount);
                    // 设置公众号
                    fans.setAccount(WxMemoryCacheClient.getAccount());
                    fansList.add(fans);
                }
                // 批处理
                fansDao.addList(fansList);
            }
        }
        return true;
    }

    // 获取用户信息接口 - 必须是开通了认证服务，否则微信平台没有开放此功能
    public AccountFans syncAccountFans(String openId, MpAccount mpAccount, boolean merge) throws WxErrorException {
        AccountFans fans = WxApiClient.syncAccountFans(openId, mpAccount);
        if (merge && null != fans) {
            AccountFans tmpFans = fansDao.getByOpenId(openId,mpAccount.getAccount());
            if (tmpFans == null) {
                fansDao.add(fans);
            } else {
                fans.setId(tmpFans.getId());
                fansDao.update(fans);
            }
        }
        return fans;
    }

    // 根据openid 获取粉丝，如果没有，同步粉丝
    public AccountFans getFansByOpenId(String openId, MpAccount mpAccount) throws WxErrorException {
        AccountFans fans = fansDao.getByOpenId(openId,mpAccount.getAccount());
        if (fans == null) {// 如果没有，添加
            fans = WxApiClient.syncAccountFans(openId, mpAccount);
            if (null != fans) {
                fansDao.add(fans);
            }
        }
        return fans;
    }

    //同步标签列表
    public boolean  syncUserTagList(MpAccount mpAccount) throws WxErrorException {
    	String url = WxApi.getUserTagList(WxApiClient.getAccessToken(mpAccount));
        logger.info("同步用户标签参消息如下:"+url);
        JSONObject jsonObject = WxApi.httpsRequest(url, HttpMethod.GET, null);
        logger.info("同步用户标签消息如下:"+jsonObject.toString());
        WxError error =WxError.fromJson(jsonObject);
        if(error.getErrorCode()!=0){
        	throw new WxErrorException(error);
        }
        JSONArray arr = jsonObject.getJSONArray("tags");//获取jsonArray对象
        //由于标签总数不会超过100，所以这里使用遍历，性能影响不会很大
        	for (int i = 0; i < arr.size(); i++) {
        		JSONObject json=arr.getJSONObject(i);
            	UserTag tag=new UserTag();
                tag.setTagId(json.getInteger("id"));
                tag.setName(json.getString("name"));
                tag.setAccount(mpAccount.getAccount());
                tag.setCount(json.getInteger("count"));
            	UserTag userTag=userTagDao.queryObject(tag);
            	//判断数据库是否存在标签，如果不存在则添加、存在就对比字段进行更新操作
                if (userTag == null) {
                    userTagDao.add(tag);
                } else {
                	userTag.setCount(tag.getCount());
                	userTag.setName(tag.getName());
                	userTagDao.update(userTag);
                }
    		}
        return true;
    }

    /**
     * 构造图片 音频 文本 视频 图文 返回
     * @param msgBase
     * @param msgRequest
     * @param mpAccount
     * @return
     */
    private String buildMsg(MsgBase msgBase, MsgRequest msgRequest, MpAccount mpAccount ){
        if(msgBase != null) {
            //依次判断消息类型
            String msgtype = msgBase.getMsgtype();
            if(msgtype.equals(MsgType.Text.toString())){
                MsgText msgText=new MsgText();
                msgText.setBaseId(msgBase.getId());
                msgText.setAccount(mpAccount.getAccount());
                msgText = msgTextDao.getByBaseId(msgText);
                return MsgXmlUtil.textToXml(WxMessageBuilder.getMsgResponseText(msgRequest, msgText));
            }else if(msgtype.equals(MsgType.Image.toString())){
                MediaFiles mediaFiles=new MediaFiles();
                mediaFiles.setBaseId(msgBase.getId());
                mediaFiles.setAccount(mpAccount.getAccount());
                mediaFiles= mediaFilesMapper.getFileBySou(mediaFiles);
                return MsgXmlUtil.imageToXml(WxMessageBuilder.getMsgResponseImage(msgRequest, mediaFiles));
            }else if(msgtype.equals(MsgType.Voice.toString())){
                MediaFiles mediaFiles=new MediaFiles();
                mediaFiles.setBaseId(msgBase.getId());
                mediaFiles.setAccount(mpAccount.getAccount());
                mediaFiles= mediaFilesMapper.getFileBySou(mediaFiles);
                return MsgXmlUtil.voiceToXml(WxMessageBuilder.getMsgResponseVoice(msgRequest, mediaFiles));
            }else if(msgtype.equals(MsgType.Video.toString())){
                MediaFiles mediaFiles=new MediaFiles();
                mediaFiles.setBaseId(msgBase.getId());
                mediaFiles.setAccount(mpAccount.getAccount());
                mediaFiles= mediaFilesMapper.getFileBySou(mediaFiles);
                return MsgXmlUtil.videoToXml(WxMessageBuilder.getMsgResponseVideo(msgRequest, mediaFiles));
            }else if(msgtype.equals(MsgType.News.toString())){
                List<MsgArticle> msgNews = msgBaseDao.listMsgNewsByBaseId(new String[]{String.valueOf(msgBase.getId())},mpAccount.getAccount());
                if (msgNews != null && msgNews.size() > 0) {
                    return MsgXmlUtil.newsToXml(WxMessageBuilder.getMsgResponseNews(msgRequest, msgNews));
                }
            }
        }
        return null;
    }
}
